/**
 * Jaxson ajax
 *
 * Copyright Remedy Interactive, licensed under Apache version 2 license:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0.html
 *
 * This notice must remain in all java source files.
 */
package org.jaxson.struts.encoder;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import javax.servlet.ServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.jaxson.struts.encoder.excel.ExcelAnnotationCache;
import org.jaxson.struts.encoder.excel.ExcelRenderer;
import org.jaxson.struts.encoder.excel.ExcelAnnotationCache.ExcelFieldDescriptor;

/**
 * Encodes a list of objects to an Excel spreadsheet.
 * 
 * @author Joe Maisel
 */
public class ExcelEncoder extends ResponseEncoder
{

	@SuppressWarnings("unused")
	private static final Log lager = LogFactory.getLog( ExcelEncoder.class );
	private ServletRequest request;
	
	/**
	 * @see org.jaxson.struts.encoder.ResponseEncoder#encode(java.io.Serializable)
	 */
	@Override
	public void encode( Serializable model ) throws EncodingException
	{
		if( !(model instanceof List<?>) )
			throw new IllegalArgumentException( "Excel Response Encoding can only be performed on classes extending java.util.List." );
		
		List<?> l = (List<?>)model;
		
		if( l == null || l.size() == 0 )
			return;
		
		try
		{
			Object o = l.get(0);
			Class<?> cls = o.getClass();
			ExcelAnnotationCache.parse( o.getClass() );
			
			ExcelFieldDescriptor desc = ExcelAnnotationCache.get( o.getClass() );
			ExcelRenderer renderer = (ExcelRenderer)desc.renderer.newInstance(); 
			renderer.setRequest (getRequest());
			desc.getters = renderer.expose(desc.getters);
			
			HSSFWorkbook wb = new HSSFWorkbook();
			renderer.setWorkbook( wb );	
			
			//Excel Limitation is 65535 rows per sheet.
			//Breaking the data into few different pages.
			float totalRows=l.size ();
			float totalPages=totalRows/65535;
			
			int noOfSheets=(int) Math.floor(totalPages+1.0d);
			int row1=0;
			
			sheetLoop:
			for(int sheetNo=1;sheetNo<=noOfSheets;sheetNo++){
				HSSFSheet sheet = wb.createSheet((renderer.getSheetName()==null?"":renderer.getSheetName())+" Page " + sheetNo);   
				renderer.setDefaultSheet( sheet );  
				createHeaders(renderer,sheet);
				int sheetRow=0;
				
				// render data
				for( int row=row1; row < l.size(); row++ )
				{
					//Shift to new page
					if (sheetRow >= 65535) {
						row1=row;
						continue sheetLoop;
					}
					createRows(renderer,sheet,desc,cls,l,row,sheetRow++);
				}
			}
		
			wb.write (getOutputStream());
		}
		catch( Exception e )
		{
			e.printStackTrace();
			throw new EncodingException(e);
		}
	}
	
	/**
	 * Create header row if the renderer has a header row
	 * @param renderer
	 * @param sheet
	 */
	private void createHeaders(ExcelRenderer renderer,HSSFSheet sheet)
	{
		if(renderer.isHeaderRow ()){
			
			 //the header row: centered text in 48pt font
			HSSFRow headerRow = sheet.createRow(0);
		    headerRow.setHeightInPoints(12.75f);
		    
		    // Freeze just one row
		    sheet.createFreezePane( 0, 1, 0, 1 );

		    for( int col=0; col<renderer.getHeaderNames ().size(); col++ )
			{
		    	HSSFCell cell = headerRow.createCell(col);
		        Object value= renderer.getHeaderNames ().get( col ).toUpperCase ();
		        renderer.setCurrentCell( cell, value, renderer.getHeaderNames ().get( col ),true );
		    }
		}
	}
	
	/**
	 * Create the row reflecting the values from the field Descriptor
	 * @param renderer
	 * @param sheet
	 * @param desc
	 * @param cls
	 * @param l
	 * @param row
	 * @param sheetRow
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private void createRows(ExcelRenderer renderer, HSSFSheet sheet, ExcelFieldDescriptor desc, Class<?> cls, List<?> l, int row, int sheetRow) 
	throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		sheet = renderer.getActiveSheet();				
		HSSFRow r = sheet.createRow( sheetRow+1 );
		renderer.setCurrentRow( r );
		
		for( int col=0; col<desc.getters.size(); col++ )
		{
			HSSFCell cell = r.createCell( col );
			Object value = cls.getMethod( desc.getters.get( col ) ).invoke( l.get( row ) );
			renderer.setCurrentCell( cell, value, desc.getters.get( col ),false );
		}
	}

	/**
	 * @return the request
	 */
	public ServletRequest getRequest ()
	{
		return request;
	}

	/**
	 * @param request the request to set
	 */
	public void setRequest (ServletRequest request)
	{
		this.request = request;
	}

}
